{
	UCE - cross-platform game engine written in delphi.
	Copyright (c) 2009, Shutko Vitaly

	This file is part of UCE.

	UCE is free software; you can redistribute it and/or
	modify it under the terms of the GNU Lesser General Public
	License as published by the Free Software Foundation; either
	version 2.1 of the License, or (at your option) any later version.

	UCE is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public
	License along with this library; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
}

unit uSndOAL;

interface

uses
  uOpenAL, uTyp, uSnd;

type
  TSndBufOAL = class(TSndBuf)
    buf, src: TALuint;

    procedure Play(loop: Boolean = False); override;
    procedure Pause; override;
    procedure Stop; override;
    procedure Resume; override;
    function Playing: Boolean; override;

    procedure SetVolume(v: Integer); override;
    procedure SetPan(p: Integer); override;
    
    function Upload(data: TBytes; chans, bps, freq: Integer): Boolean; override;
    function Dublicate: TSndBuf; override;

    constructor Create;
    destructor Destroy; override;
  end;

  TSndManOAL = class(TSndMan)
    dev: TALCdevice;
    cont: TALCcontext;

    function Init: Boolean; override;

    constructor Create;
    destructor Destroy; override;
  end;

implementation

uses
  uVar;

//////////////////////////////////////////////////////////////////////////////
///  TSndBufOAL
//////////////////////////////////////////////////////////////////////////////

constructor TSndBufOAL.Create;
begin
  inherited Create;

	buf := 0;
  src := 0;
end;

destructor TSndBufOAL.Destroy;
begin
  if src <> 0 then alDeleteSources(1, @src);
  if buf <> 0 then alDeleteBuffers(1, @buf);
end;

procedure TSndBufOAL.Play(loop: Boolean = False);
begin
  if src <> 0 then
  begin
    playStart := tim.GetTime;

    alSourcei(src, AL_LOOPING, Integer(loop));
    alSourceRewind(src);
    alSourcePlay(src);
  end;
end;

procedure TSndBufOAL.Pause;
begin
  if src <> 0 then
    alSourcePause(src);
end;

procedure TSndBufOAL.Resume;
begin
  if src <> 0 then
    alSourcePlay(src);
end;

procedure TSndBufOAL.Stop;
begin
  if src <> 0 then
    alSourceStop(src);
end;

function TSndBufOAL.Playing: Boolean;
var
  state: TALint;
begin
  Result := False;

  if src <> 0 then
  begin
    alGetSourcei(src, AL_SOURCE_STATE, @state);

    Result := (state = AL_PLAYING) or (state = AL_PAUSED);
  end;
end;

procedure TSndBufOAL.SetVolume(v: Integer);
begin
  if v < 0 then v := 0;
  if v > 100 then v := 100;

  volume := v;

  if src <> 0 then
    alSourcef(src, AL_GAIN, v / 100);
end;

procedure TSndBufOAL.SetPan(p: Integer);
begin
  if src <> 0 then
    //TODO: think, Sokal, think!)
end;

function TSndBufOAL.Upload(data: TBytes; chans, bps, freq: Integer): Boolean;
var
  fmt: Cardinal;
begin
  Result := False;

  alGenBuffers(1, @buf);
  
  fmt := 0;

  if chans = 1 then
  begin
    case bps of
      8: fmt := AL_FORMAT_MONO8;
      16: fmt := AL_FORMAT_MONO16;
    end;
  end else
  begin
    case bps of
      8: fmt := AL_FORMAT_STEREO8;
      16: fmt := AL_FORMAT_STEREO16;
    end;
  end;

  if fmt <> 0 then alBufferData(buf, fmt, @data[0], Length(data), freq) else
  begin
    //error
    Exit;
  end;

  alGenSources(1, @src);
  alSourcei(src, AL_BUFFER, buf);

  Result := True;
end;

function TSndBufOAL.Dublicate: TSndBuf;
begin
  Result := TSndBufOAL.Create;
  
  TSndBufOAL(Result).buf := buf;

  alGenSources(1, @TSndBufOAL(Result).src);
  alSourcei(TSndBufOAL(Result).src, AL_BUFFER, buf);
end;

//////////////////////////////////////////////////////////////////////////////
///  TSndManOAL
//////////////////////////////////////////////////////////////////////////////

constructor TSndManOAL.Create;
begin
  inherited Create;

  dev := nil;
  cont := nil;
end;

destructor TSndManOAL.Destroy;
begin
  if cont <> nil then
  begin
    alcMakeContextCurrent(nil);
    alcDestroyContext(cont);
  end;

  if dev <> nil then alcCloseDevice(dev);
end;

function TSndManOAL.Init: Boolean;
begin
  Result := False;

  if not InitOpenAL then
  begin
    //error
    Exit;
  end;

  dev := alcOpenDevice(nil);

  if dev = nil then
  begin
    //error
    Exit;
  end;

  cont := alcCreateContext(dev, nil);

  if cont = nil then
  begin
    //error
    Exit;
  end;

  alcMakeContextCurrent(cont);

  opened := True;

  Result := True;
end;

end.
